package com.dayu.finecomm.protocol;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.tuple.Pair;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dayu.finecomm.protocol.codec.InvalidStream;
import com.dayu.finecomm.protocol.codec.Protocol;
import com.dayu.finecomm.protocol.codec.ProtocolDecoder;
import com.dayu.finecomm.protocol.pojo.TPacket;

import io.netty.buffer.ByteBuf;

/**
 * @ClassName: Protocols
 * @Description:协议辍
 * @author: 戴卫国
 * @date: 2018年2月23日 下午4:21:14
 * 
 * @Copyright: 2018 dayu Inc. All rights reserved.
 */
public abstract class Protocols {

	static Logger LOGGER = LoggerFactory.getLogger(Protocols.class);

	@SuppressWarnings("unchecked")
	public static List<ProtocolDecoder<TPacket>> protocols(Set<String> protocols) {

		List<ProtocolDecoder<TPacket>> ps = new ArrayList<>();

		Reflections refs = new Reflections("com.dayu.finecomm.protocol");

		for (Class<?> clazz : refs.getTypesAnnotatedWith(Protocol.class)) {

			String name = clazz.getAnnotation(Protocol.class).value().toUpperCase();
			if (protocols.contains(name)) {

				try {

					ps.add((ProtocolDecoder<TPacket>) clazz.newInstance());
					LOGGER.info("Added protocol: " + clazz.getName());

				} catch (InstantiationException | IllegalAccessException e) {
					throw new RuntimeException(e);
				}
			}
		}

		return ps;
	}

	public static Pair<ProtocolDecoder<TPacket>, Pair<Integer, TPacket>> decode(
			List<ProtocolDecoder<TPacket>> protocols, ByteBuf in) throws InvalidStream {

		if (in.readableBytes() <= 0)
			return null;

		for (ProtocolDecoder<TPacket> c : protocols) {

			try {

				int header = c.checkStart(in.slice());
				if (header < 0) {
					continue;
				}

				int length = c.check(in.slice(header, in.readableBytes()));

				if (length <= 0) {
					continue;
				}

				TPacket s = c.unpack(in.slice(header, length));

				if (s != null) {
					return Pair.of(c, Pair.of(header + length, s));
				}

			} catch (InvalidStream e) {
				throw e;
			} catch (Exception e) {
				LOGGER.warn("协议解析出错。", e);
			}
		}

		return null;
	}
}
